#include "h_cc2640r2f_iics.h"

#include <ti/drivers/PIN.h>
#include <ti/devices/cc26x0r2/driverlib/ioc.h>

#include "h_cc2640r2f_define.h"

#define H_CC2640R2F_IICS_SCL                    IOID_4
#define H_CC2640R2F_IICS_SDA                    IOID_5


const PIN_Config H_CC2640R2F_IICS_OutputCFG[] = 
{
    /* The Green LED on test board */
    H_CC2640R2F_IICS_SCL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    /* The Read LED on test board */
    H_CC2640R2F_IICS_SDA | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    
    PIN_TERMINATE
};

static PIN_Handle H_CC2640R2F_IICS_OutputHandle = NULL;
static PIN_State H_CC2640R2F_IICS_OutputState;

#define H_CC2640R2F_IICS_SDA_H()                  PIN_setOutputValue(H_CC2640R2F_IICS_OutputHandle, H_CC2640R2F_IICS_SDA, 1)
#define H_CC2640R2F_IICS_SDA_L()                  PIN_setOutputValue(H_CC2640R2F_IICS_OutputHandle, H_CC2640R2F_IICS_SDA, 0)

#define H_CC2640R2F_IICS_SDA_Read()               PIN_getInputValue(H_CC2640R2F_IICS_SDA)

#define H_CC2640R2F_IICS_SCL_H()                  PIN_setOutputValue(H_CC2640R2F_IICS_OutputHandle, H_CC2640R2F_IICS_SCL, 1)
#define H_CC2640R2F_IICS_SCL_L()                  PIN_setOutputValue(H_CC2640R2F_IICS_OutputHandle, H_CC2640R2F_IICS_SCL, 0)

#define H_CC2640R2F_IICS_Delay(x)                 CPUdelay(x)

#define H_CC2640R2F_IICS_Debug                    H_CC2640R2F_UART_Debug

#define H_CC2640R2F_IICS_ReadOP                   0x01
#define H_CC2640R2F_IICS_WriteOP                  0x00

#define H_CC2640R2F_SCL_HTime                     5
#define H_CC2640R2F_SCL_LTime                     5

signed char H_CC2640R2F_IICS_Init(void)
{
    if(NULL != H_CC2640R2F_IICS_OutputHandle)
    {
        return (-1);
    }
    H_CC2640R2F_IICS_OutputHandle = PIN_open(&H_CC2640R2F_IICS_OutputState, \
                                                H_CC2640R2F_IICS_OutputCFG);
    
    
    //H_CC2640R2F_IICS_SCL_H();
    //H_CC2640R2F_IICS_SDA_H();
    
    return 0;
}

static void h_cc2640r2f_iics_sda_in(void)
{
    PIN_setConfig(H_CC2640R2F_IICS_OutputHandle, \
                    PIN_BM_OUTPUT_MODE | PIN_BM_INPUT_MODE, \
                        H_CC2640R2F_IICS_SDA | PIN_GPIO_OUTPUT_DIS | PIN_INPUT_EN | PIN_NOPULL);
}

static void h_cc2640r2f_iics_sda_out(void)
{
    PIN_setConfig(H_CC2640R2F_IICS_OutputHandle, \
                    PIN_BM_OUTPUT_MODE | PIN_BM_INPUT_MODE, \
                        H_CC2640R2F_IICS_SDA | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX);
}

static void h_cc2640r2f_start(void)
{
    H_CC2640R2F_IICS_SDA_L();
    H_CC2640R2F_IICS_Delay(5);
    H_CC2640R2F_IICS_SCL_L();
}

static void h_cc2640r2f_stop(void)
{
    H_CC2640R2F_IICS_SCL_H();
    H_CC2640R2F_IICS_Delay(5);
    H_CC2640R2F_IICS_SDA_H();
}

static void h_cc2640r2f_writebyte(unsigned char data)
{
    unsigned char i = 0;
    
    //H_CC2640R2F_IICS_SCL_L();
    for(i = 0U; i < 8U; i++)
    {
        if(data & 0x80)
        {
            H_CC2640R2F_IICS_SDA_H();
        }
        else
        {
            H_CC2640R2F_IICS_SDA_L();
        }
        
        H_CC2640R2F_IICS_SCL_H();
        data <<= 1;
        H_CC2640R2F_IICS_Delay(H_CC2640R2F_SCL_HTime);
        H_CC2640R2F_IICS_SCL_L();
    }
    
}

static unsigned char h_cc2640r2f_readbyte(void)
{
    unsigned char data = 0U;
    unsigned char i = 0U;
    
    h_cc2640r2f_iics_sda_in();
    
    //H_CC2640R2F_IICS_SCL_L();
    //H_CC2640R2F_IICS_Delay(5);
    
    for(; i < 8; i++)
    {
        data <<= 1;
        
        H_CC2640R2F_IICS_SCL_H();
        if(H_CC2640R2F_IICS_SDA_Read())
        {
            data |= 0x01;
        }
        else
        {
            data |= 0x00;
        }
        H_CC2640R2F_IICS_SCL_H();
        
        H_CC2640R2F_IICS_SCL_L();
        H_CC2640R2F_IICS_Delay(H_CC2640R2F_SCL_LTime);
    }
    
    h_cc2640r2f_iics_sda_out();
    return data;
}

static void h_cc2640r2f_sendack(unsigned char ack)
{
    H_CC2640R2F_IICS_SCL_L();
    if(H_CC2640R2F_IICS_ACK == ack)
    {
        H_CC2640R2F_IICS_SDA_L();
    }
    else
    {
        H_CC2640R2F_IICS_SDA_H();
    }
    H_CC2640R2F_IICS_SCL_L();
    
    H_CC2640R2F_IICS_SCL_H();
    H_CC2640R2F_IICS_Delay(5);
    
    H_CC2640R2F_IICS_SCL_L();
}

static signed char h_cc2640r2f_readack(void)
{
    unsigned char ack = 0U;
    h_cc2640r2f_iics_sda_in();
    
    //H_CC2640R2F_IICS_SCL_L();
    //H_CC2640R2F_IICS_Delay(5);
    H_CC2640R2F_IICS_SCL_H();
    
    if(H_CC2640R2F_IICS_SDA_Read())
    {
        ack = H_CC2640R2F_IICS_NACK;
    }
    else
    {
        ack = H_CC2640R2F_IICS_ACK;
    }
    
    H_CC2640R2F_IICS_SCL_L();
    h_cc2640r2f_iics_sda_out();
    
    return ack;
}

signed char H_CC2640R2F_IICS_ReadBuff(unsigned char addr, unsigned char reg, unsigned char *rdata, unsigned short len)
{
    signed char rslt = 0;
    
    h_cc2640r2f_start();
    h_cc2640r2f_writebyte(addr | H_CC2640R2F_IICS_WriteOP);
    
    rslt = h_cc2640r2f_readack();
    
    
    
    
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-1);
    }
    
    h_cc2640r2f_writebyte(reg);
    rslt = h_cc2640r2f_readack();
    
    h_cc2640r2f_stop();
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        return (-2);
    }
    
    
    
    h_cc2640r2f_start();
    h_cc2640r2f_writebyte(addr | H_CC2640R2F_IICS_ReadOP);
    
    rslt = h_cc2640r2f_readack();
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-3);
    }
    
    len--;
    while(len--)
    {
        *rdata++ = h_cc2640r2f_readbyte();
        h_cc2640r2f_sendack(H_CC2640R2F_IICS_ACK);
    }
    
    *rdata++ = h_cc2640r2f_readbyte();
    h_cc2640r2f_sendack(H_CC2640R2F_IICS_NACK);
    
    h_cc2640r2f_stop();
    return 0;
}

signed char H_CC2640R2F_IICS_WriteByte(unsigned char addr, unsigned char reg, unsigned char data)
{
    signed char rslt = 0;
    
    h_cc2640r2f_start();
    h_cc2640r2f_writebyte(addr | H_CC2640R2F_IICS_WriteOP);
    
    rslt = h_cc2640r2f_readack();
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-1);
    }
    
    h_cc2640r2f_writebyte(reg);
    rslt = h_cc2640r2f_readack();
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-2);
    }
    
    
    h_cc2640r2f_writebyte(data);
    if(H_CC2640R2F_IICS_ACK != h_cc2640r2f_readack())
    {
        h_cc2640r2f_stop();
        return (-3);
    }
    
    h_cc2640r2f_stop();
    return 0;
}

signed char H_CC2640R2F_IICS_WriteBuff(unsigned char addr, unsigned char reg, unsigned char *wdata, unsigned short len)
{
    signed char rslt = 0;
    
    h_cc2640r2f_start();
    h_cc2640r2f_writebyte(addr | H_CC2640R2F_IICS_WriteOP);
    
    rslt = h_cc2640r2f_readack();
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-1);
    }
    
    h_cc2640r2f_writebyte(reg);
    rslt = h_cc2640r2f_readack();
    
    if(H_CC2640R2F_IICS_ACK != rslt)
    {
        h_cc2640r2f_stop();
        return (-2);
    }
    
    
    while(len--)
    {
        h_cc2640r2f_writebyte(*wdata++);
        if(H_CC2640R2F_IICS_ACK != h_cc2640r2f_readack())
        {
            h_cc2640r2f_stop();
            return (-3);
        }
    }
    
    h_cc2640r2f_stop();
    return 0;
}

/*
signed char H_CC2640R2F_IICS_Test(void)
{
    signed char rslt = 0;
    
    unsigned char data[5];
    
//    H_CC2640R2F_IICS_Init();
    
    rslt = H_CC2640R2F_IICS_ReadBuff(0x64, 0x00, data, 5);
    
    H_CC2640R2F_IICS_Debug("The rslt is %d, data is 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\r\n", rslt, data[0], data[1], data[2], data[3], data[4]);
    
    return 0;
}
*/
